package com.mp.approvalreq.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.mp.approvalreq.dao.*;
import com.mp.approvalreq.dao.impl.*;
import com.mp.approvalreq.entity.*;
import com.mp.approvalreq.entity.dto.RequestInfoDTO;
import com.mp.approvalreq.entity.dto.TemplateDTO;
import com.mp.approvalreq.entity.message.RequestTimeMsg;
import com.mp.approvalreq.service.CommontApprovalServiceServant;
import com.mp.approvalreq.util.*;
import com.mp.hr.service.CommontHRServicePrx;
import com.mp.user.entity.User;
import com.mp.user.service.UserServicePrx;
import com.plusmoney.exception.RpcServerLogicalException;
import com.plusmoney.jdbc.util.DBUtils;
import com.plusmoney.util.DateTimeHelper;
import com.plusmoney.util.Utils;
import com.qq.tars.client.util.ServantUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 公用的审批服务
 * 考勤
 * 人事
 * 绩效
 * 任务
 * 其它模块
 */
public class CommontApprovalServantImpl implements CommontApprovalServiceServant {
    private static final Logger logger = LoggerFactory.getLogger(CommontApprovalServantImpl.class);

    /**
     * 调休的请假审批
     */
    private static final int DEFAULT_GENERATION = 4;

    /**
     * 人事查看可调休天数详情时,拉取的审批列表
     *
     * @param companyId
     * @param contactId
     * @param currentPage
     * @param dataNum
     * @return
     * @throws Throwable
     */

    @Override
    public List<RequestInfoDTO> getPageWorkDaysOffRequests(Integer companyId, Integer contactId, Integer currentPage, Integer dataNum) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectTemplateDao selectTemplateDao = DaoFactory.getSelectTemplateDao(connection);
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            UserServicePrx userService = ServantUtils.getObject(UserServicePrx.class);
            User user = userService.getUserByContactId(contactId);

            List<RequestInfoDTO> requestInfoDTOs = new ArrayList<>();

            // 所有的加班审批
            List<RequestInfoDTO> workTimeRequestDTOs = selectApprovalDao.getOverWorkTimeRequests(user.getCompanyInfoId());
            if (ListHelper.isNotNullEmpty(workTimeRequestDTOs)) {
                List<RequestTime> requestTimes = selectApprovalDao.selectRequestTimeList(ListHelper.getRequestIds(workTimeRequestDTOs));
                // 只显示已经记录了打过卡的加班审批
                for (RequestInfoDTO workTimeRequestDTO : workTimeRequestDTOs) {
                    String daysOffTimes = getDaysOffTimes(requestTimes, workTimeRequestDTO);
                    if (StringUtils.isNotEmpty(daysOffTimes)) {
                        workTimeRequestDTO.setDaysOffTimes(daysOffTimes);
                        requestInfoDTOs.add(workTimeRequestDTO);
                    }
                }
            }

            // 所有调休审批
            List<RequestInfoDTO> leaveRequestInfoDTOs = selectApprovalDao.selectDaysOffRequests(user.getCompanyInfoId());
            requestInfoDTOs.addAll(leaveRequestInfoDTOs);

            if (ListHelper.isNotNullEmpty(requestInfoDTOs)) {
                List<Integer> templateIds = ListHelper.getTemplateIdList(requestInfoDTOs);
                List<Integer> requestIds = ListHelper.getRequestIds(requestInfoDTOs);

                Map<Integer, TemplateDTO> templateDTOMap = selectTemplateDao.getTemplateDTOMap(companyId, templateIds);
                Map<Integer, List<TemplateComponent>> componentMap = componentDao.selectComponentMapByRequestIds(Utils.list2string(requestIds));

                List<RequestInfoDTO> result = new ArrayList<>();

                for (RequestInfoDTO requestInfoDTO : requestInfoDTOs) {
                    TemplateDTO templateDTO = templateDTOMap.get(requestInfoDTO.getTemplateId());
                    requestInfoDTO.setTemplateColor(templateDTO.getTemplateColor());

                    List<TemplateComponent> components = componentMap.get(requestInfoDTO.getRequestId());
                    requestInfoDTO.setComponents(components);

                    List<String> times = ApprovalUtils.getTimeList(components, requestInfoDTO.getDefaultType());
                    requestInfoDTO.setStartTime(times.get(0));
                    requestInfoDTO.setEndTime(times.get(1));

                    requestInfoDTO.setApplicantName(user.getContactName());

                    result.add(requestInfoDTO);
                }

                // 过滤
                CommontHRServicePrx hrService = ServantUtils.getObject(CommontHRServicePrx.class);
                result = filterRequestByResetTime(result, hrService.getDaysOffResetTime(companyId));

                // 排序
                sortRelatedRequests(result);

                // 分页
                requestInfoDTOs = ApprovalListUtils.getApprovalPages(result, dataNum, currentPage);
            }

            return requestInfoDTOs;
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private List<RequestInfoDTO> filterRequestByResetTime(List<RequestInfoDTO> requestInfoDTOs, String daysOffResetTime) {
        if (StringUtils.isEmpty(daysOffResetTime)) {
            return requestInfoDTOs;
        }

        return requestInfoDTOs.stream().filter(r -> r.getRequestTime().compareTo(daysOffResetTime) > 0).collect(Collectors.toList());
    }

    private void sortRelatedRequests(List<RequestInfoDTO> requestInfoDTOs) {
        if (ListHelper.isNotNullEmpty(requestInfoDTOs)) {
            requestInfoDTOs.sort((o1, o2) -> {
                String sortTime1 = o1.getRequestTime();
                String sortTime2 = o2.getRequestTime();
                if (o1.getDefaultType() == Template.LEAVE) {
                    sortTime1 = o1.getStartTime();
                }
                if (o2.getDefaultType() == Template.LEAVE) {
                    sortTime2 = o2.getStartTime();
                }

                return -sortTime1.compareTo(sortTime2);
            });
        }
    }

    /**
     * 新版调休审批
     *
     * @param companyInfoId
     * @return
     * @throws Throwable
     */
    @Override
    public List<DaysOffTimeInfo> getDaysOffTimes(Integer companyInfoId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            List<DaysOffTimeInfo> result = new ArrayList<>();
            List<DaysOffTimeInfo> daysOffTimeInfos = selectApprovalDao.selectDaysOffRequestList(companyInfoId);

            if (Utils.isNotEmpty(daysOffTimeInfos)) {
                List<Integer> requestIds = getRequestIds(daysOffTimeInfos);
                Map<Integer, List<TemplateComponent>> componentsMap = componentDao.selectComponentMapByRequestIds(Utils.list2string(requestIds));

                for (Integer requestId : componentsMap.keySet()) {
                    for (TemplateComponent component : componentsMap.get(requestId)) {
                        if (component.getType() == ContentType.DAYS_OFF_TOTAL_TIME) {
                            DaysOffTimeInfo item = JSONObject.parseObject(component.getValue(), DaysOffTimeInfo.class);
                            item.setRequestId(requestId);
                            item.setCreateTime(getCreateTime(daysOffTimeInfos, requestId));
                            result.add(item);
                        }
                    }
                }
            }
            logger.info(result.toString());

            return result;
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private String getCreateTime(List<DaysOffTimeInfo> daysOffTimeInfos, Integer requestId) {
        for (DaysOffTimeInfo daysOffTimeInfo : daysOffTimeInfos) {
            if (requestId.equals(daysOffTimeInfo.getRequestId())) {
                return daysOffTimeInfo.getCreateTime();
            }
        }
        return null;
    }

    private List<Integer> getRequestIds(List<DaysOffTimeInfo> daysOffTimeInfos) {
        List<Integer> list = new ArrayList<>(daysOffTimeInfos.size());
        for (DaysOffTimeInfo daysOffTimeInfo : daysOffTimeInfos) {
            list.add(daysOffTimeInfo.getRequestId());
        }
        return list;
    }

    /**
     * 获取调休请假审批
     *
     * @param companyId
     * @param companyInfoId
     * @return
     * @throws Throwable
     */
    @Override
    public List<RequestInfoDTO> getDaysOffLeaveRequests(Integer companyId, Integer companyInfoId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            List<RequestInfoDTO> daysOffLeaveRequests = selectApprovalDao.selectLeaveTimeRequestsByGeneration(companyInfoId, DEFAULT_GENERATION);
            if (ListHelper.isNotNullEmpty(daysOffLeaveRequests)) {
                List<RequestInfoDTO> requestInfoDTOs = new ArrayList<>(daysOffLeaveRequests.size());

                List<Integer> requestIds = ListHelper.getRequestIds(daysOffLeaveRequests);
                Map<Integer, List<TemplateComponent>> componentsMap = componentDao.selectComponentMapByRequestIds(Utils.list2string(requestIds));
                for (RequestInfoDTO daysOffLeaveRequest : daysOffLeaveRequests) {
                    if (ApprovalUtils.isWorkDaysOffRequest(componentsMap.get(daysOffLeaveRequest.getRequestId()))) {
                        requestInfoDTOs.add(daysOffLeaveRequest);
                    }
                }

                return requestInfoDTOs;
            }

            return daysOffLeaveRequests;
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    /**
     * 人事获取计入可调休时长的加班列表
     *
     * @param companyId
     * @param
     * @return
     * @throws Throwable
     */
    @Override
    public List<RequestInfoDTO> getWorkDaysRequests(Integer companyId, Integer companyInfoId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);

            List<RequestInfoDTO> result = new ArrayList<>();
            List<RequestInfoDTO> workTimeRequestDTOs = selectApprovalDao.getOverWorkTimeRequests(companyInfoId);
            // 筛选出开启了加班时长计入调休时长的模板的审批
            if (Utils.isNotEmpty(workTimeRequestDTOs)) {
                // 获取加班时长
                List<RequestTime> requestTimes = selectApprovalDao.selectRequestTimeList(ListHelper.getRequestIds(workTimeRequestDTOs));
                for (RequestInfoDTO workTimeRequestDTO : workTimeRequestDTOs) {
                    String daysOffTimes = getDaysOffTimes(requestTimes, workTimeRequestDTO);
                    if (StringUtils.isNotEmpty(daysOffTimes)) {
                        workTimeRequestDTO.setDaysOffTimes(daysOffTimes);
                        result.add(workTimeRequestDTO);
                    }
                }
            }

            return result;
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private String getDaysOffTimes(List<RequestTime> requestTimes, RequestInfoDTO requestInfoDTO) {

        for (RequestTime requestTime : requestTimes) {
            if (requestInfoDTO.getRequestId().equals(requestTime.getRequestId())) {
                return requestTime.getDaysOffTimes();
            }
        }
        return null;
    }

    /**
     * 人事获取年假详情时用于计算
     *
     * @param companyId
     * @param companyInfoId
     * @return
     * @throws Throwable
     */
    @Override
    public List<AnnualLeaveRequest> getUserAnnualLeaveRequests(Integer companyId, Integer companyInfoId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            LeaveDao leaveDao = new LeaveDaoImpl(connection);
            RequestDao requestDao = new RequestDaoImpl(connection);

            List<AnnualLeaveRequest> result = new ArrayList<>();
            List<Leave> leaves = leaveDao.selectUserLeavesByLeaveType(companyId, companyInfoId, "年假");
            if (Utils.isEmpty(leaves)) {
                return result;
            }
            Map<Integer, Leave> leaveMap = LeaveUtils.getLeaveMap(leaves);
            List<Request> requests = requestDao.selectRequestByIds(Utils.collection2string(leaveMap.keySet()));
            for (Request request : requests) {
                Leave leave = leaveMap.get(request.getRequestDkey());
                AnnualLeaveRequest annualLeaveRequest = new AnnualLeaveRequest();
                annualLeaveRequest.setRequestId(request.getRequestDkey());
                annualLeaveRequest.setFinished(request.getFinished());
                annualLeaveRequest.setCreateTime(DateTimeHelper.format(request.getCreateTimestamp(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
                annualLeaveRequest.setDurationTime(String.valueOf(leave.getDuration()));

                annualLeaveRequest.setStartTime(DateTimeHelper.format(leave.getRealStartTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
                annualLeaveRequest.setEndTime(DateTimeHelper.format(leave.getRealEndTime(), DateTimeHelper.YEAR_MONTH_DAY_HOUR_MINUTE_PATTERN));
                result.add(annualLeaveRequest);
            }
            return result;
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    /**
     * 考勤结算时,记录考勤结算出的加班审批的实际加班时长
     *
     * @param requestTimeMsg
     * @throws Throwable
     */
    @Override
    public void recordDaysOffTime(RequestTimeMsg requestTimeMsg) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            OperateTemplateDao operateTemplateDao = DaoFactory.getOperateTemplateDao(connection);

            RequestInfoDTO requestInfoDTO = selectApprovalDao.getRequestInfoDTO(requestTimeMsg.getRequestId());
            requestTimeMsg.setContactId(requestInfoDTO.getApplicant());
            requestTimeMsg.setCompanyInfoId(requestInfoDTO.getApplicantCiid());
            requestTimeMsg.setCompanyId(requestInfoDTO.getCompanyId());

            List<RequestTime> requestTimes = selectApprovalDao.selectAllRequestTime(requestTimeMsg.getRequestId());
            if (Utils.isEmpty(requestTimes)) {
                operateTemplateDao.insertDaysOffTimes(requestTimeMsg);
            } else {
                for (RequestTime requestTime : requestTimes) {
                    if (Objects.equals(requestTimeMsg.getDate(), requestTime.getDate())) {
                        operateTemplateDao.updateDaysOffTimes(requestTimeMsg);
                        return;
                    }
                }
                operateTemplateDao.insertDaysOffTimes(requestTimeMsg);
            }

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    /**
     * 外出开始-结束时间; 外出经纬度; 外出地点; 审批id, companyInfoId
     * 外出打卡偏移;
     */
    @Override
    public OutSignInfo getOutSignInfo(Integer requestId, Integer companyId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            SelectTemplateDao selectTemplateDao = DaoFactory.getSelectTemplateDao(connection);
            ComponentDao componentDao = new ComponentImpl(connection);


            RequestInfoDTO requestInfoDTO = selectApprovalDao.getRequestInfoDTO(requestId, companyId);
            TemplateDTO templateDTO = selectTemplateDao.getTemplateDTO(companyId, requestInfoDTO.getTemplateId());
            List<TemplateComponent> components = componentDao.selectComponentByRequestId(requestInfoDTO.getRequestId());
            // TODO 改成套件
            return initOutSignInfo(requestInfoDTO, templateDTO, components);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private OutSignInfo initOutSignInfo(RequestInfoDTO requestInfoDTO, TemplateDTO templateDTO, List<TemplateComponent> contents) {
        OutSignInfo outSignInfo = new OutSignInfo();
        outSignInfo.setRequestId(requestInfoDTO.getRequestId());
        if (StringUtils.isNotEmpty(requestInfoDTO.getOutPosition())) {
            String[] outPosition = requestInfoDTO.getOutPosition().split("#");
            List<String> strings = new ArrayList<>(Arrays.asList(outPosition));
            outSignInfo.setOutPosition(strings);
        }
        outSignInfo.setCompanyInfoId(requestInfoDTO.getApplicantCiid());

        if (StringUtils.isNotEmpty(templateDTO.getOutSignOffset())) {
            outSignInfo.setOutSignOffset(Integer.valueOf(templateDTO.getOutSignOffset()));
        }

        List<String> times = ApprovalUtils.getTimeList(contents, Template.OUTDOOR);
        outSignInfo.setStartTime(times.get(0));
        outSignInfo.setEndTime(times.get(1));

        for (TemplateComponent content : contents) {
            if (content.getNum() == 3) {
                if (StringUtils.isNotEmpty(content.getValue())) {
                    try {
                        outSignInfo.setLocation(JSONArray.parseArray(content.getValue(), String.class));
                    } catch (Throwable e) {
                        logger.error(e.getMessage(), e);
                        List<String> list = new ArrayList<>();
                        list.add(content.getValue());
                        outSignInfo.setLocation(list);
                    }
                }
            }
        }

        return outSignInfo;
    }

    @Override
    public BusinessTripSignInfo getBusinessTripSignInfo(Integer requestId, Integer companyId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            SelectTemplateDao selectTemplateDao = DaoFactory.getSelectTemplateDao(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            RequestInfoDTO requestInfoDTO = selectApprovalDao.getRequestInfoDTO(requestId, companyId);
            TemplateDTO templateDTO = selectTemplateDao.getTemplateDTO(companyId, requestInfoDTO.getTemplateId());
            // TODO 改成套件
            List<TemplateComponent> components = componentDao.selectComponentByRequestId(requestInfoDTO.getRequestId());

            return initBusinessTripSignInfo(requestInfoDTO, templateDTO, components);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private BusinessTripSignInfo initBusinessTripSignInfo(RequestInfoDTO requestInfoDTO, TemplateDTO templateDTO, List<TemplateComponent> contents) {
        BusinessTripSignInfo businessTripSignInfo = new BusinessTripSignInfo();
        businessTripSignInfo.setRequestId(requestInfoDTO.getRequestId());
        businessTripSignInfo.setCompanyId(requestInfoDTO.getCompanyId());
        businessTripSignInfo.setCompanyInfoId(requestInfoDTO.getApplicantCiid());
        if (StringUtils.isNotEmpty(templateDTO.getOutSignOffset())) {
            businessTripSignInfo.setOutSignOffset(Integer.valueOf(templateDTO.getOutSignOffset()));
        }

        if (StringUtils.isNotEmpty(requestInfoDTO.getOutPosition())) {
            LinkedList<String> strings = new LinkedList<>(Arrays.asList(requestInfoDTO.getOutPosition().split("#")));
            if (isExistSrcPosition(contents)) {
                businessTripSignInfo.setSrcPosition(getBusinessSrcPosition(contents));
                businessTripSignInfo.setSrcLngLat(strings.getFirst());
                // 移除出发地的经纬度
                strings.removeFirst();
            }
            if (isExistDescPosition(contents)) {
                businessTripSignInfo.setDescPositions(JSONArray.parseArray(getBusinessDescPosition(contents), String.class));
                businessTripSignInfo.setDescLngLats(strings);
            }
        }

        List<String> times = ApprovalUtils.getTimeList(contents, Template.BUSINESS_TRIP);
        businessTripSignInfo.setStartTime(times.get(0));
        businessTripSignInfo.setEndTime(times.get(1));

        logger.info("出差信息:{}", businessTripSignInfo);
        return businessTripSignInfo;
    }

    @Override
    public void recordAddressOfBusinessTrip(BusinessTripSignInfo businessTripSignInfo) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            checkTripInfo(businessTripSignInfo);
            logger.info(businessTripSignInfo.toString());

            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            OperateApprovalDao operateApprovalDao = DaoFactory.getOperateApprovalDao(connection);
            OutdoorDao outdoorDao = new OutdoorDaoImpl(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            RequestInfoDTO requestInfoDTO = selectApprovalDao.getRequestInfoDTO(businessTripSignInfo.getRequestId());
            List<TemplateComponent> components = componentDao.selectComponentByRequestId(businessTripSignInfo.getRequestId());
            if (isExistDescPosition(components)) {
                throw new RpcServerLogicalException(1, "已存在目的地, 无需回填");
            }

            String destCoordinate;
            if (isExistSrcPosition(components) && StringUtils.isNotEmpty(requestInfoDTO.getOutPosition())) {
                destCoordinate = requestInfoDTO.getOutPosition() + "#" + businessTripSignInfo.getDescLngLats().get(0);
            } else {
                destCoordinate = businessTripSignInfo.getDescLngLats().get(0);
            }
            logger.info("原先经纬度:{} | 现在经纬度:{}", requestInfoDTO.getOutPosition(), destCoordinate);
            operateApprovalDao.updateOutPosition(destCoordinate, businessTripSignInfo.getRequestId());

            String destAddress = JSONArray.toJSONString(businessTripSignInfo.getDescPositions());
            // 回填目的地
            operateApprovalDao.updateContent(destAddress, businessTripSignInfo.getRequestId(), 3, ContentType.LOCATION);

            try {
                outdoorDao.updateDestInfo(destAddress, destCoordinate, businessTripSignInfo.getRequestId());
            } catch (SQLException e) {
                EmailUtils.sendErrorMail("报错啦", JSONObject.toJSONString(e));
            }
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private String getBusinessSrcPosition(List<TemplateComponent> components) {
        for (TemplateComponent component : components) {
            if (component.getNum() == 2) {
                return component.getValue();
            }
        }
        return null;
    }

    private String getBusinessDescPosition(List<TemplateComponent> components) {
        for (TemplateComponent component : components) {
            if (component.getNum() == 3) {
                return component.getValue();
            }
        }
        return null;
    }

    private boolean isExistSrcPosition(List<TemplateComponent> components) {
        for (TemplateComponent component : components) {
            // 是否存在出发地， 出发地可不填写,不填写时不会传该组件值
            if (component.getNum() == 2 && StringUtils.isNotEmpty(component.getValue())) {
                return true;
            }
        }
        return false;
    }

    private boolean isExistDescPosition(List<TemplateComponent> components) {
        for (TemplateComponent component : components) {
            // 是否存在目的地， 目的地可设置为不填写, 不填写时前端默认为: ""
            if (component.getNum() == 3 && StringUtils.isNotEmpty(component.getValue())) {
                return true;
            }
        }
        return false;
    }

    private void checkTripInfo(BusinessTripSignInfo businessTripSignInfo) {
        if (Utils.isNull(businessTripSignInfo)) {
            throw new RpcServerLogicalException(1, "出差详情不能为空");
        }

        if (Utils.isNull(businessTripSignInfo.getRequestId())) {
            throw new RpcServerLogicalException(1, "审批id不能为空");
        }

        if (Utils.isNull(businessTripSignInfo.getCompanyId())) {
            throw new RpcServerLogicalException(1, "公司id不能为空");
        }

        if (Utils.isNull(businessTripSignInfo.getCompanyInfoId())) {
            throw new RpcServerLogicalException(1, "申请人不能为空");
        }

        if (Utils.isEmpty(businessTripSignInfo.getDescLngLats())) {
            throw new RpcServerLogicalException(1, "出差经纬度不能为空");
        }

        if (Utils.isEmpty(businessTripSignInfo.getDescPositions())) {
            throw new RpcServerLogicalException(1, "出差目的地点不能为空");
        }
    }

    /**
     * 记录外出地点
     */
    @Override
    public void recordOutAddress(OutSignInfo signInfo) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            check(signInfo);
            logger.info("signInfo:{}", signInfo);
            OperateApprovalDao operateApprovalDao = DaoFactory.getOperateApprovalDao(connection);
            OutdoorDao outdoorDao = new OutdoorDaoImpl(connection);

            // 经纬度存字符串逗号隔开
            String destCoordinate = signInfo.getOutPosition().get(0);
            operateApprovalDao.updateOutPosition(destCoordinate, signInfo.getRequestId());

            // 地点存json数组形式
            String destAddress = JSONArray.toJSONString(signInfo.getLocation());
            operateApprovalDao.updateContent(destAddress, signInfo.getRequestId(), 3, ContentType.LOCATION);

            try {
                outdoorDao.updateDestInfo(destAddress, destCoordinate, signInfo.getRequestId());
            } catch (SQLException e) {
                logger.error(e.getMessage(), e);
                EmailUtils.sendErrorMail("报错了呢", JSONObject.toJSONString(e));
            }

        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void check(OutSignInfo signInfo) {
        if (Utils.isNull(signInfo)) {
            throw new RpcServerLogicalException(1, "外出详情不能为空");
        }

        if (Utils.isNull(signInfo.getRequestId())) {
            throw new RpcServerLogicalException(1, "审批id不能为空");
        }

        if (ListHelper.isNullEmpty(signInfo.getOutPosition())) {
            throw new RpcServerLogicalException(1, "外出经纬度不能为空");
        }

        if (ListHelper.isNullEmpty(signInfo.getLocation())) {
            throw new RpcServerLogicalException(1, "外出地点不能为空");
        }
    }

    @Override
    @Deprecated
    public DaysOffInfo getUserDaysOffRequest(Integer requestId, Integer companyId) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            SelectApprovalDao selectApprovalDao = DaoFactory.getSelectApprovalDao(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            RequestInfoDTO request = selectApprovalDao.getRequestInfoDTO(requestId, companyId);
            List<TemplateComponent> templateComponents = componentDao.selectComponentByRequestId(requestId);
            List<String> times = ApprovalUtils.getTimeList(templateComponents, Template.DAYS_OFF);

            return initDaysOffInfo(request, times);
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private DaysOffInfo initDaysOffInfo(RequestInfoDTO request, List<String> times) throws ParseException {
        SimpleDateFormat sdf = DateUtils.getSimpleDateFormat(DateUtils.YMDHM);
        DaysOffInfo daysOffInfo = new DaysOffInfo();
        daysOffInfo.setCompanyId(request.getCompanyId());
        daysOffInfo.setRequestId(request.getRequestId());
        daysOffInfo.setCompanyInfoId(request.getApplicantCiid());
        daysOffInfo.setStartTime(sdf.parse(times.get(0)).getTime());
        daysOffInfo.setEndTime(sdf.parse(times.get(1)).getTime());
        return daysOffInfo;
    }

    @Override
    @Deprecated
    public List<Request> getRequestListByTime(Integer companyInfoId, Integer templateType, String startTime, String endTime) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            logger.info("companyInfoId:{} | type:{} | startTime:{} | endTime:{}", companyInfoId, templateType, startTime, endTime);
            checkType(templateType);
            SelectApprovalDao selectApprovalDao = new SelectApprovalDaoImpl(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            List<Request> requests = selectApprovalDao.selectRequestListByTemplateType(companyInfoId, templateType, getFinishedStatusByTemplateType(templateType));
            if (Utils.isNotEmpty(requests)) {
                List<Integer> requestIds = ListHelper.getRequestIdList(requests);
                Map<Integer, List<TemplateComponent>> contentsMap = componentDao.selectComponentMapByRequestIds(Utils.list2string(requestIds));
                for (Request request : requests) {
                    request.setComponentsContent(contentsMap.get(request.getRequestDkey()));
                }
                return filterRequestsByTime(requests, startTime, endTime, templateType);
            }

            return new ArrayList<>();
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }
    }

    private void checkType(Integer templateType) {
        if (templateType == Template.BUSINESS_TRIP
                || templateType == Template.WORK_OVERTIME
                || templateType == Template.OUTDOOR) {
            return;
        }
        throw new RpcServerLogicalException(1, "暂不支持的审批类型");
    }

    private String getFinishedStatusByTemplateType(Integer templateType) {
        if (templateType == Template.BUSINESS_TRIP
                || templateType == Template.WORK_OVERTIME
                || templateType == Template.OUTDOOR) {
            return "0,1,2";
        }
        return "1,2";
    }

    private List<Request> filterRequestsByTime(List<Request> requests, String startTime, String endTime, Integer templateType) {
        List<Request> list = new ArrayList<>();
        for (Request request : requests) {
            if (Utils.isNotEmpty(request.getComponentsContent())) {
                List<String> requestTimes = ApprovalUtils.getTimeList(request.getComponentsContent(), templateType);
                // start < 出差结束 && end > 出差开始， 说明两者有交集
                if (startTime.compareTo(requestTimes.get(1)) < 0 && endTime.compareTo(requestTimes.get(0)) > 0) {
                    list.add(request);
                }
            }
        }
        return list;
    }

    @Override
    public void fillOutdoorAddress(Outdoor outdoor) throws Throwable {
        Connection connection = DBUtils.getConnection(DbName.APPROVALREQ);
        try {
            OutdoorDao outdoorDao = new OutdoorDaoImpl(connection);
            ComponentDao componentDao = new ComponentImpl(connection);

            componentDao.updateContent(outdoor.getDestAddress(), outdoor.getRequestId(), 3, ContentType.LOCATION);
            outdoorDao.updateDestInfo(outdoor.getDestAddress(), outdoor.getDestCoordinate(), outdoor.getRequestId());
        } catch (Throwable e) {
            DBUtils.rollback(connection);
            logger.error(e.getMessage(), e);
            throw ErrorUtils.throwError(e);
        } finally {
            DBUtils.release(connection);
        }

    }
}
